/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                  *
 *        *************************************************         *
 *        *©2021-2031 HT  Corporation All rights reserved *         *
 *        *************************************************         *
 *                                                                  *
 * FileName    : h_pthread.c                                        *
 *                                                                  *
 * Author      : linus                                              *
 *                                                                  *
 * Email       : luoyaojun@haitutech.com                            *
 *                                                                  *
 * Date        : 2023-2-9                                           *
 *                                                                  *
 * Description :                                                    *
 *                                                                  *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "h_pthread.h"
#include "pthread/pthread_work.h"

static HT_VOID ht_pthread_work_list(struct ht_pthread *pthd_core)
{
	struct pthread_work *pwork_list = NULL;

	pwork_list = pthd_core->pwork_head;

	while (pwork_list)
	{
		printf("pthread work list %s ======= %p\n", pwork_list->work_name?:"null", pwork_list);
		pwork_list = pwork_list->next;
	}
	
}

static HT_VOID ht_pthread_work_insert(struct ht_pthread *pthd_core, struct pthread_work *pwork)
{
    struct pthread_work **pwork_list = NULL;

    pthread_mutex_lock(&pthd_core->work_list_mutex);

    pwork_list = &pthd_core->pwork_head;
	
    while (*pwork_list)
        pwork_list = &((*pwork_list)->next);

    *pwork_list = pwork;

    pthread_mutex_unlock(&pthd_core->work_list_mutex);
}

static struct pthread_work *ht_pthread_work_findout(struct ht_pthread *pthd_core, const char *name)
{
    struct pthread_work *pwork_list = NULL;

    pthread_mutex_lock(&pthd_core->work_list_mutex);

    pwork_list = pthd_core->pwork_head;

    while (pwork_list)
    {
        if (strncmp(pwork_list->work_name, name, strlen(pwork_list->work_name)) == 0)
            break;

        pwork_list = pwork_list->next;
    }
    pthread_mutex_unlock(&pthd_core->work_list_mutex);

    return pwork_list;
}

static HT_VOID ht_pthread_work_delete_via_name(struct ht_pthread *pthd_core, const char *name)
{
    struct pthread_work **pwork_list = NULL;
    struct pthread_work *pwork_free = NULL;

    pthread_mutex_lock(&pthd_core->work_list_mutex);

    pwork_list = &pthd_core->pwork_head;

    while (*pwork_list)
    {
        pwork_free = *pwork_list;

        if (strncmp(pwork_free->work_name, name, strlen(pwork_free->work_name)) == 0)
        {
            pwork_list = &((*pwork_list)->next);
            SYS_FREE_MEM(pwork_free);
            break;
        }

        pwork_list = &((*pwork_list)->next);
    }

    pthread_mutex_unlock(&pthd_core->work_list_mutex);
}

static HT_VOID ht_pthread_work_delete(struct ht_pthread *pthd_core, struct pthread_work *pwork)
{
    struct pthread_work **pwork_list = NULL;
    struct pthread_work *pwork_free = NULL;

    pthread_mutex_lock(&pthd_core->work_list_mutex);

    pwork_list = &pthd_core->pwork_head;

    while (*pwork_list)
    {
        pwork_free = *pwork_list;

        if (pwork_free == pwork)
        {
        	*pwork_list = pwork_free->next?:NULL;
			
            SYS_FREE_MEM(pwork_free);
            break;
        }

        pwork_list = &((*pwork_list)->next);
    }
	
	ht_pthread_work_list(pthd_core);
    pthread_mutex_unlock(&pthd_core->work_list_mutex);
}

static HT_VOID *ht_pthread_work(HT_VOID *v)
{
    struct pthread_work *pwork = (struct pthread_work *)v;
    HT_S32 timeout = 0;
    HT_S32 ret = -1;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
	
    for (;pwork->work_mode;)
    {
        ret = pwork->hdl(pwork, pwork->argv);		

        pthread_testcancel(); 

        pthread_mutex_lock(&pwork->mutex);
        timeout = pwork->timeout;
        pthread_mutex_unlock(&pwork->mutex);

        if (timeout > 0)
            usleep(timeout * 1000);
    }

//    printf("pthread: work %s exiting by user callback....\n", pwork->work_name);

    // delete ...
    ht_pthread_work_delete((struct ht_pthread *)pwork->pthread, pwork);

    return NULL;
}

static HT_S32 ht_pthread_work_create(struct pthread_work *pwork)
{
    return (pthread_create(&pwork->pthd, NULL, ht_pthread_work, (HT_VOID *)pwork));
}

static HT_S32 ht_pthread_request_work(struct ht_pthread *pthd_core, const char *name, HT_PTHREAD_HANDLE hdl, HT_VOID *argv,
                               HT_S32 timeout, HT_U32 work_mode)
{
    HT_S32 ret = -1;
    struct pthread_work *pwork = NULL;

    if (!pthd_core || !name || !hdl)
        return -1;

    pwork = (struct pthread_work *)calloc(1, sizeof(struct pthread_work));

    if (!pwork)
        return -2;

    pthread_mutex_init(&pwork->mutex, NULL);

    pwork->work_name 	= name;
    pwork->hdl 			= hdl;
    pwork->argv 		= argv;
    pwork->timeout 		= timeout;
    pwork->pthread 		= (HT_HANDLE)pthd_core;
	pwork->work_mode	= !!work_mode;
	
    ret = ht_pthread_work_create(pwork);

    if (ret)
    {
        printf("ht_pthread_request work create faild %d\n", ret);
        SYS_FREE_MEM(pwork);
        return -3;
    }

    ht_pthread_work_insert(pthd_core, pwork);

    return 0;
}
							   
HT_S32 ht_pthread_request_work_once(struct ht_pthread *pthd_core, const char *name, HT_PTHREAD_HANDLE hdl,
                                        HT_VOID *argv)
{
    return (ht_pthread_request_work(pthd_core, name, hdl, argv, -1, PTHREAD_WORK_MODE_ONCE));
}

HT_S32 ht_pthread_request_work_always(struct ht_pthread *pthd_core, const char *name, HT_PTHREAD_HANDLE hdl,
                                        HT_VOID *argv)
{
    return (ht_pthread_request_work(pthd_core, name, hdl, argv, -1, PTHREAD_WORK_MODE_ALWAYS));
}

HT_S32 ht_pthread_work_stop(struct ht_pthread *pthd_core, const char *name)
{
    struct pthread_work *pwork = NULL;

    if (!pthd_core || !name)
        return -1;

    pwork = ht_pthread_work_findout(pthd_core, name);

	printf("ht_pthread_work_stop findout %s -> %p\n", name, pwork);

    if (pwork)
    {
        pthread_cancel(pwork->pthd);

        pthread_join(pwork->pthd, NULL);

        ht_pthread_work_delete(pthd_core, pwork);
    }

    return 0;
}

HT_S32 ht_pthread_init(struct ht_pthread *pthd_core, const char *name)
{
    if (!pthd_core)
        return -1;

    if (name)
        pthd_core->pthread_name = name;

    return (pthread_mutex_init(&pthd_core->work_list_mutex, NULL));
}

HT_VOID ht_pthread_set_priv(struct ht_pthread *pthd_core, HT_VOID *priv)
{
    pthd_core->priv = priv;
}

HT_S32 ht_pthread_work_set_timeout(struct ht_pthread *pthd_core, const char *name, HT_S32 timeout)
{
    struct pthread_work *pwork = NULL;

    if (!pthd_core || !name)
        return -1;

    pwork = ht_pthread_work_findout(pthd_core, name);

    if (pwork)
    {
        pthread_mutex_lock(&pwork->mutex);
        pwork->timeout = timeout;
        pthread_mutex_unlock(&pwork->mutex);
    }

    return 0;
}

HT_VOID ht_pthread_work_set_priv(struct pthread_work *pwork, HT_VOID *priv)
{
	pwork->priv = priv;
}

struct pthread_work* ht_pthread_work_get_priv(struct pthread_work *pwork)
{
	return pwork->priv;
}

struct ht_pthread *ht_pthread_work_to_pthread(struct pthread_work *pwork)
{
	return (pwork?pwork->pthread:NULL);
}

